<template>
    <div :style="$attrs.style">
        <div v-for="file in files" :key="file" style="border-bottom: 1px dashed #ccc; padding: 4px 0;">
            <MyImg v-if="file.isImage" :src="file.fileUrl" class="filebox imgbox" />
            <video v-else-if="file.isVideo" :src="getD(file.fileUrl)" controls="controls" class="filebox" :style="videoStyle"></video>
            <audio v-else-if="file.isAudio" :src="getD(file.fileUrl)" controls="controls" class="filebox"></audio>
            <div v-else-if="disabled" class="filebox filename">
                <div><a :href="getD(file.fileUrl)" :download="file.fileName" @click="downloadFile(file)">{{
        file.fileName }}</a></div>
                <div>{{ formatSize(file.fileSize) }}</div>
            </div>

            <div class="fileinfo" v-if="!disabled">
                <div class="filename">
                    <div><a :href="getD(file.fileUrl)" :download="file.fileName" @click="downloadFile(file)">{{file.fileName }}</a></div>
                    <div>{{ formatSize(file.fileSize) }}</div>
                </div>
                <div class="rightbar" v-if="!disabled">
                    <el-icon @click.stop="doRemove(file)">
                        <Delete />
                    </el-icon>
                </div>
            </div>
        </div>
        <input type="file" :multiple="allowMulti" ref="inputRef" :accept="fileAccept" v-on:change="handleChange" style="display:none" />
        <div v-if="!disabled && files.length < max" v-on:click="doUpload" style="display: inline-block;">
            <el-icon class="pic-icon" v-if="percent == 0">
                <Plus />
            </el-icon>
            <el-progress v-else type="circle" :percentage="percent" width="100px" class="pic-icon" />
        </div>
    </div>
</template>

<script setup>
import { ref, watch, computed } from 'vue'
import store from '../store';
import _server from '../Server';
import { Plus, Delete } from '@element-plus/icons-vue'
import MyImg from '../MyImg';


const props = defineProps({
    modelValue: String,
    disabled: Boolean,
    extData: {
        type: Object,
        default: function () {
            return {
                thumbSize: 100
            }
        },
    },
    headers: {
        type: Object,
        default: function () { return {} },
    },
    ossServer: [Boolean, String],
    server: {
        type: String,
        default: "/Common/UploadFile"
    },
    max: {
        type: Number,
        default: 1
    },
    maxSize: {
        type: Number,
        default: 100
    },
    fileAccept: {
        type: String,
        default: "*.*"
    },
    videoStyle:String,
});

const emit = defineEmits(["update:modelValue", "download", "uploadOK"]);
const files = ref([]);
const percent = ref(0);
const inputRef = ref();

const fileTypes = [
    { id: 1, type: "video", exts: [".mp4", ".mov", ".avi", ".m4v", ".mpeg", ".ogm", ".wmv", ".mpg", ".webm", ".m4v", ".asx"], icon: "" },
    { id: 2, type: "audio", exts: [".mp3"], icon: "" },
    { id: 3, type: "image", exts: [".jpg", ".png", ".jpeg", ".gif", ".webp"], icon: "" },
    { id: 4, type: "doc", exts: [".xls", ".xlsx", ".doc", ".docx", ".csv", ".pdf", ".ppt", ".pptx", ".wps", ".pot", ".potx", ".txt"], icon: "" },
    { id: 5, type: "zip", exts: [".zip", ".rar", ".7z", ".gz", ".tar"], icon: "" },
    { id: 6, type: "code", exts: [".c", ".cpp", ".cs", ".java", ".py", ".go", ".vue", ".xml", ".json", ".log", ".ini", ".txt"], icon: "" },
];

const realServer = computed(() => {
    return (store.UploadServer ? store.UploadServer : props.server);
    //return _server.getUrl(props.server);
})

const allowMulti = computed(()=>{
    return (props.max - files.value.length) > 1;
});

function getKnownFileType(fn) {
    if (!fn) return null;

    let ix = fn.lastIndexOf("/");
    if (ix > -1) {
        fn = fn.substring(ix);
    }
    ix = fn.lastIndexOf(".");
    if (ix == -1) return null;

    let ext = fn.substring(ix).toLowerCase();
    if (!ext) return null;


    for (let ix in fileTypes) {
        let p = fileTypes[ix];
        if (p.exts.indexOf(ext) > -1) return p;
    }

    return null;
}

/**
 * 格式化文件大小, 输出成带单位的字符串
 * @param {Number} size 文件大小
 * @param {Number} [pointLength=2] 精确到的小数点数。
 * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。
 *    如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.
 */
function formatSize(size, pointLength, units) {
    var unit;
    units = units || ['B', 'K', 'M', 'G', 'TB'];
    while ((unit = units.shift()) && size > 1024) {
        size = size / 1024;
    }
    return (unit === 'B' ? size : size.toFixed(pointLength === undefined ? 2 : pointLength)) + unit;
};

function parseFile(url) {
    if (!url) return null;

    url = url.trim();
    if (url.length == 0) return null;

    let fn = url.split(":");
    if (fn.length == 4) {
        return {
            fileUrl: fn[0],
            fileName: fn[1],
            fileSize: fn[2],
            fileType: fn[3],
            isVideo: fn[3].startsWith("video/"),
            isAudio: fn[3].startsWith("audio/"),
            isImage: fn[3].startsWith("image/"),
        }
    }
    let ft = getKnownFileType(url);
    return {
        fileUrl: url,
        fileName: url.substring(url.lastIndexOf("/")),
        fileSize: 0,
        fileType: ft,
        isVideo: ft ? ft.type == "video" : false,
        isAudio: ft ? ft.type == "audio" : false,
        isImage: ft ? ft.type == "image" : false,
    }
}

function encodeFile(file) {
    return file.fileUrl + ":" + file.fileName + ":" + file.fileSize + ":" + file.fileType;
}


function onModelValueChange() {
    if (props.modelValue) {
        let fns = String(props.modelValue).split(";");
        let list = [];
        fns.forEach(p => {
            let file = parseFile(p);
            list.push(file)
        })
        files.value = list;
    }
}

function getD(fn) {
    let url = store.getUrl(fn);
    return url;
}

function handleSuccess(res, rawFile) {
    if (!res.success) {
        $msg.error(res.message);
        return;
    }
    let raw = rawFile.raw || rawFile;
    let file = parseFile(res.url + ":" + raw.name + ":" + raw.size + ":" + raw.type);
    files.value.push(file);

    let str = files.value.map(p => encodeFile(p)).join(";");
    emit("update:modelValue", str);
}

function beforeUpload(file) {
    if (props.disabled || files.value.length >= props.max) return false;

    if (file.size > props.maxSize * 1024 * 1024) {
        $msg.error(`超过最大尺寸(${props.maxSize}M)限制`);
        return false;
    }
    if (file.name) {
        let ft = getKnownFileType(file.name);
        if (ft) return true;
    }

    $msg.error("不支持的文件类型");
    return false;
}

function doRemove(file) {
    if (props.disabled) return;
    let ix = files.value.indexOf(file);
    if (ix != -1) {
        files.value.splice(ix, 1);
    }
    let str = files.value.map(p => encodeFile(p)).join(";");
    emit("update:modelValue", str);
}

function downloadFile(fileItem) {
    emit("download", fileItem.fileUrl, fileItem.fileName)
}

function doUpload() {
    if (props.disabled) return;
    inputRef.value.value = null;
    inputRef.value.click();
}

function handleChange(e) {
    const ufiles = e.target.files;
    if (!ufiles) return;
    let allowLen = Math.min(ufiles.length, props.max - files.value.length);
    if (allowLen > 0) {
        var ff = [];
        for (var i = 0; i < allowLen; i++) {
            ff[i] = ufiles[i];
        }
        uploadFiles(ff);
    }
}

function uploadFiles(files) {
    for (var i = 0; i < files.length; i++) {
        var file = files[i]
        if (!beforeUpload(file)) return;
    }
    if (files.length > 0) {
        uploadOne(files[0]).then(res => {
            if (files.length > 1) {
                files.splice(0, 1);
                uploadFiles(files);
            }
        });
    }
}

function uploadOne(file) {
    return postFile(file, null, file.name);
}

function postFile(rawFile, formData, fileName) {
    if (props.ossServer) {
        let pm = {
            ...props.extData,
            filename: fileName
        }
        //向服务器查询是否可以接受的文件
        return $server.call("/Common/OssSignature", pm).then(res => {
            //上传到OSS服务器上
            var data = [];
            data['key'] = 'NSO' + res.key;
            data['OSSAccessKeyId'] = res.OSSAccessKeyId;
            data['policy'] = res.policy;
            data['signature'] = res.signature;
            data['success_action_status'] = '200';
            data['Content-Disposition'] = 'attachment;filename="' + fileName + '"'
            let ossHost = "//" + res.ossHost;
            store.OSSEndPont = ossHost;
            formData = formData || new FormData();
            Object.keys(data).forEach(key => {
                formData.append(key, data[key]);
            })

            let okurl = "/NSO" + res.key;
            return post2(rawFile, formData, fileName, ossHost, okurl);
        })
    } else {
        return post2(rawFile, formData, fileName)
    }
}

function post2(rawFile, formData, fileName, host, okurl) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        const action = host ? host : (store.UploadServer ? store.UploadServer : props.server);

        if (xhr.upload) {
            xhr.upload.onprogress = function progress(e) {
                if (e.total > 0) {
                    e.percent = parseFloat((e.loaded / e.total * 100).toFixed(2))
                    percent.value = e.percent;
                }
            }
        }
        percent.value = 0
        formData = formData || new FormData();

        if (props.extData && !props.ossServer) {
            Object.keys(props.extData).forEach(key => {
                formData.append(key, props.extData[key])
            })
        }

        if (rawFile != null) {
            formData.append("file", rawFile, rawFile.name);
        }


        xhr.onerror = function error(err) {
            percent.value = 0;
            let errmsg = "上传时发生错误:" + xhr.status + "," + xhr.statusText;
            $msg.message(errmsg, "error");
            reject(xhr);
        }

        xhr.onload = function onload() {
            if (xhr.status < 200 || xhr.status >= 300) {
                percent.value = 0;
                let errmsg = "上传时发生错误:" + xhr.status + "," + xhr.statusText;
                $msg.message(errmsg, "error");
                reject(xhr);
                return
            }
            var text = xhr.responseText || xhr.response
            if (okurl) {
                text = { success: true, url: okurl, name: fileName };
            } else {
                try {
                    text = JSON.parse(text)
                } catch {

                }
            }
            percent.value = 0
            if (text.success) {
                emit("uploadOK", { url: text.url, name: text.name || fileName });
            }
            handleSuccess(text, rawFile)
            resolve(text);
        }

        xhr.open('post', action, true)

        if ('withCredentials' in xhr) {
            xhr.withCredentials = false
        }

        const headers = props.headers || {}

        for (const item in headers) {
            if (headers.hasOwnProperty(item) && headers[item] !== null) {
                xhr.setRequestHeader(item, headers[item])
            }
        }
        xhr.send(formData)
        return xhr
    });
}

watch(() => props.modelValue, () => {
    onModelValueChange();
});


onModelValueChange();

</script>

<style scoped>
.medialfile {
    width: 100%;
}

.medialfile :deep(.el-upload) {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
}

.medialfile :deep(.el-upload__input) {
    display: none !important;
}

.filebox {
    width: 100%;
    max-height: 80% !important;
    text-align: center !important;
}

.imgbox {
    width: 120px !important;
    height: 120px !important;
}

.medialfile .el-upload:hover {
    border-color: #409eff;
}

.el-icon.avatar-uploader-icon {
    font-size: 48px;
    color: #8c939d;
    max-width: 100px;
    max-height: 100px;
    text-align: center;
}

.avatar {
    max-width: 100px;
    max-height: 100px;
    display: block;
}

.fileinfo {
    display: flex;
    width: 100%;
    background-color: #f0f0f0;
}

.filename {
    flex: 1;
    display: block;
    text-align: left;
}

.rightbar {
    font-size: 24px;
}

.pic-icon {
    justify-content: center;
    align-items: center;
    position: relative;

    display: inline-block;
    font-size: 100px !important;
    border: 1px dashed #cecece;
    color: #cecece;
    width: 100px !important;
    height: 100px !important;
}
</style>